#include "VRPTW.hpp"
#include <list>
#include <cstring>
#include <cstdlib>
#include "Sol.hpp"

// unsafe strto{ld} evidemment
VRPTW::VRPTW(const std::string&	inFilename)	EXC_THROWER()
	: vrFilename(inFilename)
{
	FILE*					fp = 0;
	char					buffer[0x1000];
	char*					end_ptr = 0;
	std::list<Client*>		clients;
	
	if ((fp = fopen(inFilename.c_str(), "rt")) == 0)
	{
		std::cerr << "trying to open : " << inFilename << std::endl;
		EXC_THROW("unable to open file");
	}
	
	fgets(buffer, 0xffc, fp);
	buffer[strlen(buffer) - 1] = '\0';
	vrVehicleCapacity = strtol(buffer, &end_ptr, 10);
	vrNClients = 0;
	fgets(buffer, 0xffc, fp);

	while (!feof(fp))
	{
		unsigned	i;
		double		x;
		double		y;
		double		demand;
		double		ready_time;
		double		due_date;
		double		service_time;
		char*		nend_ptr = 0;
		
		memset(buffer, 0, 0x1000 * sizeof(char));
		fgets(buffer, 0xffc, fp);
		if (strlen(buffer) == 0)
			break;
		buffer[strlen(buffer) - 1] = '\0';
		
		i = strtol(buffer, &end_ptr, 10);
		x = strtod(end_ptr, &nend_ptr);
		end_ptr = nend_ptr;
		y = strtod(end_ptr, &nend_ptr);
		end_ptr = nend_ptr;
		demand = strtod(end_ptr, &nend_ptr);
		end_ptr = nend_ptr;
		ready_time = strtod(end_ptr, &nend_ptr);
		end_ptr = nend_ptr;
		due_date = strtod(end_ptr, &nend_ptr);
		end_ptr = nend_ptr;
		service_time = strtod(end_ptr, &nend_ptr);
        if (i == 1)
        {
            vrDepot = new Depot(x, y, due_date);
        }
        else
        {
            clients.push_back(new Client(*this, (i - 2), x, y, demand, ready_time, due_date, service_time));
            vrNClients++;
        }
	}
	
	vrClients = new Client*[vrNClients + 2];
    
    unsigned  j;
    
	for (j = 0 ; j < vrNClients ; j++)
	{
		vrClients[j] = clients.front();
		clients.pop_front();
	}
    
    vrClients[j++] = new Client(*this, 0xFFFFFFFF, vrDepot->veX, vrDepot->veY, 0., 0., vrDepot->deDueDate, 0.);
    vrClients[j] = new Client(*this, 0xFFFFFFFF, vrDepot->veX, vrDepot->veY, 0., 0., vrDepot->deDueDate, 0.);
    vrDistances = new double[(vrNClients + 1) * (vrNClients + 1)];
    vrDistancesByClient = new std::multimap<double, unsigned>[(vrNClients + 1)];
    vrGains = new double[(vrNClients + 1) * (vrNClients + 1)];
    
    // pre calcul des distances sur le graphe complet (les clients ne vont pas bouger)
    for (unsigned j = 0 ; j < (vrNClients + 1) ; j++)
    {
        for (unsigned k = 0 ; k < (vrNClients + 1) ; k++)
        {
            const Vertex* v1 = 0;
            const Vertex* v2 = 0;
            
            
            if (j == vrNClients)
                v1 = vrDepot;
            else
                v1 = vrClients[j];
                
            if (k == vrNClients)
                v2 = vrDepot;
            else
                v2 = vrClients[k];
            
            // doublons dans ces arrays
            vrDistances[j * (vrNClients + 1) + k] = Vertex::getDistanceBetween(*v1, *v2);
			vrDistancesByClient[j].insert(std::pair<double, unsigned>(vrDistances[j * (vrNClients + 1) + k], k));
            vrGains[j * (vrNClients + 1) + k] = 
                        Vertex::getDistanceBetween(*v1, *vrDepot)  // c(i,0)
                    +   Vertex::getDistanceBetween(*vrDepot, *v2)  // c(0,j)
                    -   Vertex::getDistanceBetween(*v1, *v2);    // c(i,j)
        }
    }
    
    vrNextRDepot = vrNClients;
    
    
	fclose(fp);
}

VRPTW::~VRPTW()
{
	for (unsigned j = 0 ; j < (vrNClients + 2) ; j++)
		delete vrClients[j];
	
    delete vrDepot;
	delete [] vrClients;
    delete [] vrDistances;
    delete [] vrDistancesByClient;
    delete [] vrGains;
}

void		VRPTW::printDotPoint(FILE* inFile, unsigned inClient) const
{
	const double	factor = 1.;
	
	assert(inFile != 0);
	
	if (inClient == DEPOT)
	{
		fprintf (inFile, "%u [ label=\"D\" pos=\"%d,%d!\" size=\"4,4!\" ];\n",
				DEPOT, (int)(vrDepot->veX * factor), (int)(vrDepot->veY * factor));
	}
	else
	{
		fprintf(inFile, "%u [ label=\"%2u\" pos=\"%d,%d!\" size=\"4,4!\" ];\n", 
				inClient, (inClient + 1), (int)(vrClients[inClient]->veX * factor), (int)(vrClients[inClient]->veY * factor)
				);
	}
}

void	VRPTW::print() const
{
    
    std::cout << "ID (fid) - X     - Y      - DEMAND    - READYTIME   - DUEDATE     - SERVICETIME   " << std::endl;
	std::cout << vrDepot->toString() << std::endl;
    for (unsigned i = 0 ; i < vrNClients ; i++)
		std::cout << vrClients[i]->toStr() << std::endl;
}

VRPTW::RealizableStatus			VRPTW::checkInsert(	const ServiceInfo&	inI,
													const ServiceInfo&	inJ,
													unsigned			inN) const
{
	double	val = 0.;
	
	assert(inN != size());
	assert(inN != size() + 1);
	assert(inN != 0xFE);
	assert(inN != 0xFFFFFFFF);
	
	val = inJ.siAcceptableGlobalDelay - inI.siDepartureDate;
	
    std::cout << "((x2 - x1) = " << val << ") <= " << vrClients[inN]->clReadyTime - vrClients[inN]->clDueDate - vrClients[inJ.siClient]->clDueDate << std::endl;
    std::cout << "((x2 - x1) = " << val << ") >= " << vrClients[inN]->clReadyTime - vrClients[inN]->clDueDate + vrClients[inJ.siClient]->clDueDate << std::endl;
    
	// inJ.siClient => DEPOT segfault (logique)
	if (val <= (vrClients[inN]->clReadyTime - vrClients[inN]->clDueDate - vrClients[inJ.siClient]->clDueDate))
		return (VRPTW::REALIZABLE_LTE);		// PAS ENCORE TESTEE
	else if (val >= (vrClients[inN]->clReadyTime - vrClients[inN]->clDueDate + vrClients[inJ.siClient]->clDueDate))
		return (VRPTW::REALIZABLE_GTE);
	
	return (VRPTW::REALIZABLE_NONE);
}

VRPTW::RealizableStatus        VRPTW::checkInsert2(  const ServiceInfo&  inI,
                                                     const ServiceInfo&  inJ,
                                                     unsigned            inN,
                                                     unsigned            inCapacity) const
{
    if (vrClients[inN]->clDemand + inCapacity > this->getVehicleCapacity())
        return (VRPTW::REALIZABLE_NONE);
    
// (1)	si (xN.DueDate - DISTANCE(xI → xN) <= xI.DepartureDate)
    if ((vrClients[inN]->clDueDate - getDistanceBetween(inI.siClient, inN)) <= inI.siDepartureDate)
        return (VRPTW::REALIZABLE_NONE);
        
//(2)	(xN.ReadyTime + xN.ServiceTime + DISTANCE(xN → xJ)	> xJ.DueDate)
    if ((vrClients[inN]->clReadyTime + vrClients[inN]->clServiceTime + getDistanceBetween(inN, inJ.siClient)) > vrClients[inJ.siClient]->clDueDate)
        return (VRPTW::REALIZABLE_NONE);
    
//(3)    si (xN.ReadyTime + xNServiceTime + DISTANCE(xN → xJ)  
//				> 	xJ.AcceptableGlobalDelay + xI.DepartureDate)

    if (vrClients[inN]->clReadyTime + vrClients[inN]->clServiceTime + getDistanceBetween(inN, inJ.siClient) > (inJ.siAcceptableGlobalDelay + inI.siDepartureDate))
        return (VRPTW::REALIZABLE_NONE);
    
    return (VRPTW::REALIZABLE_LTE);
}

VRPTW::RealizableStatus        VRPTW::checkInsert3(  const ServiceInfo&  inI,
                                                     const ServiceInfo&  inJ,
                                                     unsigned            inN,
                                                     unsigned            inCapacity) const
{	
	double 	n_arrival;
	double	n_wait;
	double	n_stay;
	double	n_departure;
	double 	i_n_j_total;
	
    if (vrClients[inN]->clDemand + inCapacity > this->getVehicleCapacity())
        return (VRPTW::REALIZABLE_NONE);
    
// (1)	si (xN.DueDate - DISTANCE(xI → xN) <= xI.DepartureDate)
    if ((n_arrival = inI.siDepartureDate + getDistanceBetween(inI.siClient, inN)) > vrClients[inN]->clDueDate)
        return (VRPTW::REALIZABLE_NONE);
    
    n_wait = vrClients[inN]->clReadyTime - n_arrival;
    if (n_wait < 0.)
		n_wait = 0.;
    n_stay = n_wait + vrClients[inN]->clServiceTime;
    n_departure = n_arrival + n_stay;
    i_n_j_total = getDistanceBetween(inI.siClient, inN) + n_stay + getDistanceBetween(inN, inJ.siClient);
    
    if (i_n_j_total > (getDistanceBetween(inI.siClient, inJ.siClient) + inJ.siAcceptableGlobalDelay))
		return (VRPTW::REALIZABLE_NONE);
		
	return (VRPTW::REALIZABLE_LTE);
}

// checkInsert4 => refaire tout comme avant, sans soucier de ces delays moisis
VRPTW::RealizableStatus		VRPTW::checkInsert4( 	const Vehicle& 	inV,
													unsigned		inI,
													unsigned		inJ,
													unsigned		inN,
													unsigned		inCCapacity) const
{
	// 1. creation vehicule fantome dans buffer de 32Ko sur stack (8 pages, je pense que ca devrait suffire)
	unsigned char	buffer[0x8000];
	unsigned char*	succs = &buffer[0];
	unsigned char*	preds = &buffer[this->size() + 2];
	ServiceInfo*	infos = (ServiceInfo*)&buffer[(this->size() + 2) << 1];
	unsigned char*  tournees_by_client = &buffer[((this->size() + 2) << 1) + (this->size() + 2) * sizeof(ServiceInfo)];
	Vehicle			vehicle((VRPTW*)this, inV.veId);
	
	// 2. cpy
	memcpy(succs, inV.veSuccs, (this->size() + 2) * sizeof(unsigned char));
	memcpy(preds, inV.vePreds, (this->size() + 2) * sizeof(unsigned char));
	memcpy(infos, inV.veInfos, (this->size() + 2) * sizeof(ServiceInfo));
	memcpy(tournees_by_client, inV.veTourneeByClient, (this->size() * sizeof(unsigned char)));
	
	// 3. bind
	vehicle.veSuccs = succs;
	vehicle.vePreds = preds;
	vehicle.veInfos = infos;
	vehicle.veTourneeByClient = tournees_by_client;
	
	return (vehicle.insert2(inN, inI, inJ)) ? VRPTW::REALIZABLE_LTE : VRPTW::REALIZABLE_NONE;	
}



